package com.stylefeng.guns.core.util;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.lang3.RandomUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 
 * @author haqiaolong 2016年2月23日 下午2:49:51
 * @since 5.0.0
 */
public class HttpClientUtil {
	
	private Logger log = LoggerFactory.getLogger(HttpClientUtil.class);

    private CloseableHttpClient            httpClient;

    private volatile static HttpClientUtil clientUtil;

    private static int                     HTTP_TIMEOUT_INMILLIONSECONDS = 10000;

    private static final int               POOL_SIZE                     = 20;

    private static ThreadPoolExecutor      asyncExecutor                 = RetryUtil.createThreadPoolExecutor();

    public static void setHttpTimeoutInMillionSeconds(int httpTimeoutInMillionSeconds) {
        HTTP_TIMEOUT_INMILLIONSECONDS = httpTimeoutInMillionSeconds;
    }

    public static synchronized HttpClientUtil getHttpClientUtil() {
        if (null == clientUtil) {
            synchronized (HttpClientUtil.class) {
                if (null == clientUtil) {
                    clientUtil = new HttpClientUtil();
                }
            }
        }
        return clientUtil;
    }

    public HttpClientUtil(){
        initApacheHttpClient();
    }

    public void destroy() {
        destroyApacheHttpClient();
    }

    // 创建包含connection pool与超时设置的client
    private void initApacheHttpClient() {
        RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(HTTP_TIMEOUT_INMILLIONSECONDS)
            .setConnectTimeout(HTTP_TIMEOUT_INMILLIONSECONDS)
            .setConnectionRequestTimeout(HTTP_TIMEOUT_INMILLIONSECONDS)
            .setStaleConnectionCheckEnabled(true)
            .build();

        httpClient = HttpClientBuilder.create()
            .setMaxConnTotal(POOL_SIZE)
            .setMaxConnPerRoute(POOL_SIZE)
            .setDefaultRequestConfig(requestConfig)
            .build();
    }

    private void destroyApacheHttpClient() {
        try {
            if (httpClient != null) {
                httpClient.close();
                httpClient = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static HttpGet getGetRequest() {
    	HttpGet httpget = new HttpGet();
    	httpget.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); 
        httpget.setHeader("Accept-Language", "zh-cn,zh;q=0.5"); 
        httpget.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1)"); 
        //httpget.setHeader("Accept-Encoding", "gzip, deflate"); 
        httpget.setHeader("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7"); 
       // httpget.setHeader("Host", "www.baidu.com"); 
        //httpget.setHeader("Connection", "Keep-Alive");  
        return httpget;
    }

    public static HttpPost getPostRequest() {
        return new HttpPost();
    }

    public static HttpPut getPutRequest() {
        return new HttpPut();
    }

    public static HttpDelete getDeleteRequest() {
        return new HttpDelete();
    }

    public String executeAndGet(HttpRequestBase httpRequestBase) throws Exception {
        HttpResponse response;
        String entiStr = "";
        try {
            response = httpClient.execute(httpRequestBase);

            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                System.err.println("请求地址：" + httpRequestBase.getURI() + ", 请求方法：" + httpRequestBase.getMethod()
                                   + ",STATUS CODE = " + response.getStatusLine().getStatusCode());

                throw new Exception("Response Status Code : " + response.getStatusLine().getStatusCode());
            } else {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    entiStr = EntityUtils.toString(entity, Consts.UTF_8);
                } else {
                    throw new Exception("Response Entity Is Null");
                }
            }
        } catch (Exception e) {
            throw e;
        }

        return entiStr;
    }

    public String executeAndGetWithRetry(final HttpRequestBase httpRequestBase, final int retryTimes,
                                         final long retryInterval) {
        try {
            return RetryUtil.asyncExecuteWithRetry(new Callable<String>() {

                @Override
                public String call() throws Exception {
                    return executeAndGet(httpRequestBase);
                }
            }, retryTimes, retryInterval, true, HTTP_TIMEOUT_INMILLIONSECONDS + 1000, asyncExecutor);
        } catch (Exception e) {
        	log.error("error!" + e);
        	return null;
        }
    }

    public String executeAndGetWithFailedRetry(final HttpRequestBase httpRequestBase, final int retryTimes,
                                               final long retryInterval) {
        try {
        	Thread.sleep(RandomUtils.nextInt(1, 5) * 100);
            return RetryUtil.asyncExecuteWithRetry(new Callable<String>() {

                @Override
                public String call() throws Exception {
                    return executeAndGet(httpRequestBase);
                }
            }, retryTimes, retryInterval, true, HTTP_TIMEOUT_INMILLIONSECONDS + 1000, asyncExecutor);
        } catch (Exception e) {
        	log.error("error!" + e);
        	return null;
        }
    }

}
